package com.hivekion.common.file;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileUtils {

    @Value("${uploadPath.projectfile}")
    public static String storePath;

    /**
     * 文件类型
     *
     * @param suffix
     * @return
     */
    public static Integer fileType(String suffix) {
        int fileType = -1;
        String suffixLower = suffix.toLowerCase();
        if (suffixLower.contains(".png")
                || suffixLower.contains(".jpg")
                || suffixLower.contains(".gif")
                || suffixLower.contains(".bmp")) {
            fileType = 1;
        }
        if (suffixLower.contains(".mp4")
                || suffixLower.contains(".wmv")
                || suffixLower.contains(".rmvb")
                || suffixLower.contains(".avi")) {
            fileType = 2;
        }
        if (suffixLower.contains(".mp3")
                || suffixLower.contains(".wav")) {
            fileType = 3;
        }
        if (suffixLower.contains(".doc")
                || suffixLower.contains(".docx")
                || suffixLower.contains(".xls")
                || suffixLower.contains(".xlsx")
                || suffixLower.contains(".ppt")
                || suffixLower.contains(".pdf")) {
            fileType = 4;
        }
        if (suffixLower.contains(".html")) {
            fileType = 5;
        }
        return fileType;
    }

    /**
     * 文件路径
     *
     * @param fileType
     * @return
     */
    public static String filePath(Integer fileType) {
        String filePath = "";
        if (fileType == 1) {
            filePath = "Image";
        }
        if (fileType == 2) {
            filePath = "Video";
        }
        if (fileType == 3) {
            filePath = "Voice";
        }
        if (fileType == 4) {
            filePath = "File";
        }
        if (fileType == 5) {
            filePath = "Html";
        }
        return filePath;
    }

    /**
     * 文件下载类型
     *
     * @param suffix
     * @return
     */
    public static String contentType(String suffix) {
        String contentType = "application/octet-stream";
        String suffixLower = suffix.toLowerCase();
        if (suffixLower.contains(".png")) {
            contentType = "image/png";
        }
        if (suffixLower.contains(".jpg")) {
            contentType = "application/x-jpg";
        }
        if (suffixLower.contains(".gif")) {
            contentType = "image/gif";
        }
        if (suffixLower.contains(".bmp")) {
            contentType = "application/x-bmp";
        }
        if (suffixLower.contains(".mp4")) {
            contentType = "video/mpeg4";
        }
        if (suffixLower.contains(".wmv")) {
            contentType = "video/x-ms-wmv";
        }
        if (suffixLower.contains(".rmvb")) {
            contentType = "application/vnd.rn-realmedia-vbr";
        }
        if (suffixLower.contains(".avi")) {
            contentType = "video/avi";
        }
        if (suffixLower.contains(".mp3")) {
            contentType = "audio/mp3";
        }
        if (suffixLower.contains(".wav")) {
            contentType = "audio/wav";
        }
        if (suffixLower.contains(".doc")) {
            contentType = "application/msword";
        }
        if (suffixLower.contains(".docx")) {
            contentType = "application/msword";
        }
        if (suffixLower.contains(".xls")) {
            contentType = "application/x-xls";
        }
        if (suffixLower.contains(".xlsx")) {
            contentType = "application/x-xls";
        }
        if (suffixLower.contains(".ppt")) {
            contentType = "application/x-ppt";
        }
        if (suffixLower.contains(".pdf")) {
            contentType = "application/pdf";
        }
        if (suffixLower.contains(".html")) {
            contentType = "text/html";
        }
        if (suffixLower.contains(".zip")) {
            contentType = "application/octet-stream";
        }
        return contentType;
    }

    /**
     * 下载文件
     *
     * @param fullPath
     */
    public static void download(String fullPath, HttpServletResponse response) {
        File file = new File(fullPath);
        String fileName = file.getName();
        String suffix = fullPath.substring(fullPath.lastIndexOf("."));
        String contentType = contentType(suffix);
        try {
            // 输出响应正文的输出流
            OutputStream out;
            // 读取本地文件的输入流
            FileInputStream in = new FileInputStream(file);
            response.reset();
            // 设置响应正文的MIME类型
            response.setContentLength((int) file.length());
            response.setContentType(contentType + ";charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;"
                    + " filename=" + fileName);
            // 把本地文件发送给客户端
            out = response.getOutputStream();
            int byteRead = 0;
            byte[] buffer = new byte[1024];
            while ((byteRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, byteRead);
            }
            in.close();
            out.flush();
            out.close();
        } catch (Exception ex) {

        } finally {

        }
    }

    /**
     * 批量下载文件--打包成zip
     *
     * @param fullPaths
     */
    public static void downloads(List<String> fullPaths, String fileName, HttpServletResponse response) {
        List<File> files = new ArrayList<>();
        try {
            for (String fullPath :
                    fullPaths) {
                File file = new File(fullPath);
                files.add(file);
            }
            byte[] buf = new byte[1024];
            int len;
            BufferedOutputStream bufferedInputStream = new BufferedOutputStream(response.getOutputStream());
            response.reset();
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;"
                    + " filename=" + fileName);
            ZipOutputStream zipOutputStream = new ZipOutputStream(bufferedInputStream);
            for (int i = 0; i < files.size(); i++) {
                FileInputStream in = new FileInputStream(files.get(i));
                zipOutputStream.putNextEntry(new ZipEntry(files.get(i).getName()));
                while ((len = in.read(buf)) > 0) {
                    zipOutputStream.write(buf, 0, len);
                }
                zipOutputStream.closeEntry();
                in.close();
            }
            zipOutputStream.flush();
            zipOutputStream.close();
            bufferedInputStream.flush();
            bufferedInputStream.close();
        } catch (Exception ex) {

        }
    }

    public static String md5HashCode(String filePath) throws FileNotFoundException {
        FileInputStream fis = new FileInputStream(filePath);
        return md5HashCode32(fis);
    }

    private static String md5HashCode(InputStream fis) {
        try {
            // 拿到一个MD5转换器,如果想使用SHA-1或SHA-256，则传入SHA-1,SHA-256
            MessageDigest md = MessageDigest.getInstance("MD5");

            // 分多次将一个文件读入，对于大型文件而言，比较推荐这种方式，占用内存比较少。
            byte[] buffer = new byte[1024];
            int length = -1;
            while ((length = fis.read(buffer, 0, 1024)) != -1) {
                md.update(buffer, 0, length);
            }
            fis.close();
            // 转换并返回包含16个元素字节数组,返回数值范围为-128到127
            byte[] md5Bytes = md.digest();
            BigInteger bigInt = new BigInteger(1, md5Bytes); // 1代表绝对值
            return bigInt.toString(16); // 转换为16进制
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 计算文件32位md5值
     *
     * @param fis 输入流
     * @return
     */
    public static String md5HashCode32(InputStream fis) {
        try {
            //拿到一个MD5转换器,如果想使用SHA-1或SHA-256，则传入SHA-1,SHA-256
            MessageDigest md = MessageDigest.getInstance("MD5");

            //分多次将一个文件读入，对于大型文件而言，比较推荐这种方式，占用内存比较少。
            byte[] buffer = new byte[1024];
            int length = -1;
            while ((length = fis.read(buffer, 0, 1024)) != -1) {
                md.update(buffer, 0, length);
            }
            fis.close();

            //转换并返回包含16个元素字节数组,返回数值范围为-128到127
            byte[] md5Bytes = md.digest();
            StringBuffer hexValue = new StringBuffer();
            for (int i = 0; i < md5Bytes.length; i++) {
                int val = ((int) md5Bytes[i]) & 0xff;//解释参见最下方
                if (val < 16) {
                    hexValue.append("0");
                }
                //这里借助了Integer类的方法实现16进制的转换
                hexValue.append(Integer.toHexString(val));
            }
            return hexValue.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }


    /**
     * <h1>获取指定文件夹下所有文件，不含文件夹</h1>
     *
     * @param dirFilePath 文件夹路径
     * @return
     */
    public static List<File> getAllFile(String dirFilePath) {
        if (StringUtils.isBlank(dirFilePath)) {
            return null;
        }
        return getAllFile(new File(dirFilePath));
    }

    /**
     * <h1>获取指定文件夹下所有文件，不含文件夹</h1>
     *
     * @param dirFile 文件夹
     * @return
     */
    public static List<File> getAllFile(File dirFile) {
        // 如果文件夹不存在或着不是文件夹，则返回 null
        if (Objects.isNull(dirFile) || !dirFile.exists() || dirFile.isFile()) {
            return null;
        }

        File[] childrenFiles = dirFile.listFiles();
        if (Objects.isNull(childrenFiles) || childrenFiles.length == 0) {
            return null;
        }
        List<File> files = new ArrayList<>();
        for (File childFile : childrenFiles) {
            // 如果时文件，直接添加到结果集合
            if (childFile.isFile()) {
                files.add(childFile);
            } else {
                // 如果是文件夹，则将其内部文件添加进结果集合
                List<File> cFiles = getAllFile(childFile);
                if (Objects.isNull(cFiles) || cFiles.isEmpty()) {
                    continue;
                }
                files.addAll(cFiles);
            }

        }
        return files;
    }

    /**
     * 复制文件
     *
     * @param oldFile
     * @param newFile
     */
    public static void copy(File oldFile, File newFile) {
        try (InputStream in = new FileInputStream(oldFile);
             OutputStream out = new FileOutputStream(newFile)) {
            byte[] arr = new byte[1024];
            int len;
            while ((len = in.read(arr)) != -1) {
                out.write(arr, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 普通压缩文件
     *
     * @param resourcesPath
     * @throws Exception
     */
    public static void createZipFile(String resourcesPath) {
        try {
            File fileToZip = new File(resourcesPath);
            if (fileToZip.getName().endsWith(".zip")) {
                return;
            }
            FileOutputStream outputStream = new FileOutputStream(fileToZip.getPath() + ".zip");
            CheckedOutputStream cos = new CheckedOutputStream(outputStream, new CRC32());
            ZipOutputStream out = new ZipOutputStream(cos, StandardCharsets.UTF_8);
            zipDirectory(fileToZip, "", out);

            out.flush();
            out.close();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 压缩方法，重复压缩之后，会出现问题，现优化如下<br>
     * @param fileToZip
     * @param fileName
     * @param zipOut
     * @throws IOException
     */
    public static void zipDirectory(File fileToZip, String fileName, ZipOutputStream zipOut) {
        try {
            if (fileToZip.isHidden()) {
                return;
            }
            if (fileToZip.isDirectory()) {
                fileName = fileName.length() == 0 ? "" : fileName + "/";
                File[] children = fileToZip.listFiles();
                for (File childFile : children) {
                    zipDirectory(childFile, fileName + childFile.getName(), zipOut);
                }
                return;
            }
            FileInputStream fis = new FileInputStream(fileToZip);
            ZipEntry zipEntry = new ZipEntry(fileName);
            zipOut.putNextEntry(zipEntry);
            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {
                zipOut.write(bytes, 0, length);
            }
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除目录以及文件
     *
     * @param file
     */
    public static void deleteFile(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                deleteFile(files[i]);
            }
        }
        file.delete();
    }

}
